package core.proto;

import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;

import app.exceptions.CollabVizException;
import core.util.logging.UserLogWriter;
import core.util.logging.constants.Action;
import core.util.logging.constants.Location;

class MotionCanvas extends JLabel
{
    private static final long serialVersionUID = 2996021722629046464L;

    private final ImageIcon DELETE_ICON = new ImageIcon( Util.getResourceUrl( "/img/delete.jpg" ) );

    private final int CANVAS_WIDTH = Util.getFavoritesWidth();

    private final int CANVAS_HEIGHT = Util.getFavoritesHeight();

    private List<Item> dropList = new ArrayList<Item>();

    boolean isListFull = false;

    public MotionCanvas( Color backgroundColor ) {
        setBackground( backgroundColor );
        setSize( CANVAS_WIDTH, CANVAS_HEIGHT );
        this.setOpaque( true );
//        addMouseMotionListener( new MouseListenerHandler() );
        addMouseListener( new MouseListenerHandler(this) );

        // create Arrange button
        // JButton button = new JButton("Arrange");
        JButton button = new JButton( new ImageIcon( Util.getResourceUrl( "/img/arrangebutton.png" ) ) );
        button.setBackground( Color.white );
        button.setMargin( new Insets( 0, 0, 0, 0 ) );
        // button.setBorderPainted( false );
        button.setBorder( null );
        button.addMouseListener( new ButtonMouseListener() );
        this.setLayout( new FlowLayout( FlowLayout.LEFT ) );
        this.add( button );
    }

    @Override
    public void paintComponent( Graphics g ) {
        if( isListFull() )
            return;

        super.paintComponent( g );
        Graphics2D g2D = (Graphics2D) g;
        drawList( g2D, dropList );
    }

    private void drawList( Graphics2D g2D, List<Item> dropList ) {
        for( Item item : dropList ) {
            int x = item.getX();
            int y = item.getY();
            IconData data = (IconData) item.getNode().getUserObject();
            CaseNode caseNode = (CaseNode) data.getObject();
            ImageIcon icon = (ImageIcon) data.m_icon;
            String nodeName = caseNode.toString();

            // draw icon
            if( item.isSelectedIcon() ) {
                g2D.drawImage( DELETE_ICON.getImage(), x, y, Color.white, this );
                item.setSelectedIcon( false );
            } else {
                g2D.drawImage( icon.getImage(), x, y, Color.white, this );
            }

            // draw label
            if( item.isSelectedLabel() ) {
                g2D.setColor( Color.BLUE );
                g2D.drawString( nodeName, x + 20, y + 20 );
                item.setSelectedLabel( false );
            } else {
                g2D.setColor( Color.BLACK );
                g2D.drawString( nodeName, x + 20, y + 20 );
            }
        }
    }

    /**
     * The method is invoked by importDate method of FileTransferHandler when a
     * tree node dropped
     * 
     * @param mouseX
     * @param mouseY
     * @param node
     */
    public void addDroppedNode( int mouseX, int mouseY,
            DefaultMutableTreeNode node ) {
    	UserLogWriter.log( node.toString(), Location.Favorites, Action.DROP.toString());
        for( Item item : dropList ) {
            if( item.getNode() == node ) {
                JOptionPane.showMessageDialog( null,
                        "The item is already in the list.", "Duplicated",
                        JOptionPane.WARNING_MESSAGE );
                return;
            }
        }
        dropList.add( new Item( mouseX, mouseY, node ) );
        repaint();
    }

    /********************************************************************
     * The following methods are invoked by mouse action handles
     ********************************************************************/

    private boolean isTargetIcon = false;

    private boolean isTargetLabel = false;

    private Item draggingItem = null;

    private final int COLUMN_WIDTH = 280;

    /** return the item at location (mouseX, mouseY) */
    private Item findTarget( int mouseX, int mouseY ) {
        isTargetIcon = false;
        isTargetLabel = false;
        List<Item> list = dropList;
        Item targetItem = null;

        for( Item item : list ) {
            isTargetIcon = item.isIconPosition( mouseX, mouseY );
            if( isTargetIcon ) {
                targetItem = item;
                break;
            }
            isTargetLabel = item.isLabelPosition( mouseX, mouseY );
            if( isTargetLabel ) {
                targetItem = item;
                break;
            }
        }
        return targetItem;
    }

    private void doDelete( Item item ) {
        dropList.remove( item );
        repaint();
        // log delete action
        DefaultMutableTreeNode node = item.getNode();
        IconData icon = (IconData) node.getUserObject();
        CaseNode case_node = (CaseNode) icon.getObject();
    }

    private void doOpenFile( Item item ) throws CollabVizException {
        // invoke tree expand
        System.out.println( "invoke tree expand here" );
        AllComponents.getFileTree().expandTree( item.getNode() );

        // invoke open doc
        System.out.println( "invoke open doc here" );
        DefaultMutableTreeNode node = item.getNode();
        IconData icon = (IconData) node.getUserObject();
        CaseNode case_node = (CaseNode) icon.getObject();
        if( case_node.isVisited() == false ) {
            case_node.setVisited( true );
            icon.useExpandedIcon();
            AllComponents.canvas.repaint();
            AllComponents.directory.repaint();
        }
        System.out.println( case_node.getAbsolutePath() );
        // AllComponents.getDocumentPane().openDoc(openNode);
        try {
            AllComponents.fileTreeUpdateDoc( case_node, true, 1 );

        } catch( IOException e ) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private void doArrange() {
        int column = 0;
        int x = this.CANVAS_WIDTH - COLUMN_WIDTH;
        int y = 1;
        for( Item item : dropList ) {
            item.setX( x );
            item.setY( y );
            if( y + 25 <= 130 ) { // this.CANVAS_HEIGHT) {
                y = y + 25;
            } else {
                if( x - this.COLUMN_WIDTH > 0 ) {
                    x = x - this.COLUMN_WIDTH + (column + 1);
                    y = 1;
                    column++;
                } else {
                    JOptionPane
                            .showMessageDialog(
                                    null,
                                    "Too many items in the list. It's more than the area could arrange. Please delete some items from the list.",
                                    "Too many items",
                                    JOptionPane.WARNING_MESSAGE );
                    break;
                }
            }
        }
        repaint();
    }

    public boolean isListFull() {
        if( isListFull ) {
            JOptionPane.showMessageDialog( null,
                    "List is full, please delete first.", "List Full",
                    JOptionPane.WARNING_MESSAGE );
            return true;
        } else {
            return false;
        }
    }

    /**
     * MouseListenerHandler Class
     */
    class MouseListenerHandler extends MouseAdapter
    {
    	JComponent parent;
    	public MouseListenerHandler(JComponent parent){
    		this.parent = parent;
    	}
    	
    	
        public void mouseDragged( MouseEvent e ) {
            System.out.println( "dragging..." );
            int mouseX = e.getX();
            int mouseY = e.getY();
            if( draggingItem == null ) {
                draggingItem = findTarget( mouseX, mouseY );
                UserLogWriter.log(draggingItem.getCaseName(), Location.Favorites, Action.DRAGSTART);
            }
            if( draggingItem != null && isTargetLabel ) {
                draggingItem.setX( mouseX - draggingItem.dragingX );
                draggingItem.setY( mouseY - draggingItem.dragingY );
                repaint();
                // log dragging file action
            }
        }

        public void mouseMoved( MouseEvent e ) {
            //System.out.println( "mouse moving..." );
            int mouseX = e.getX();
            int mouseY = e.getY();

            Item targetItem = findTarget( mouseX, mouseY );
            // if (targetItem != null) {
            repaint();
            // }
            return;
        }

        @Override
        public void mouseClicked( MouseEvent e ) {
            int mouseX = e.getX();
            int mouseY = e.getY();

            Item targetItem = findTarget( mouseX, mouseY );
            if( targetItem != null ) {
                System.out.println( "found target ..." );

                getRootPane().setCursor(
                        Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) );
                if( isTargetIcon ) {
                	UserLogWriter.log2(targetItem.getCaseName(), Location.Favorites, Action.DELETE.toString());
                	doDelete( targetItem );
                } else if( isTargetLabel ) {
                    int count = e.getClickCount();
                    if( count >= 1 ) {// single click, can change to double if
                                      // needed
                        System.out.println( "doOpenFile" );
                        // Add how to open the file here
                        try {
                            doOpenFile( targetItem );
                        } catch( CollabVizException e1 ) {
                            e1.printStackTrace();
                        }
                    }
                    UserLogWriter.log(targetItem.getCaseName(), Location.Favorites, Action.getClicks(e.getClickCount()));
                } 
                getRootPane().setCursor( Cursor.getDefaultCursor() );
            } else {
            	UserLogWriter.log("", Location.Favorites, Action.getClicks(e.getClickCount()));
            }
            return;
        }

        @Override
        public void mousePressed( MouseEvent e ) {
        }

        @Override
        public void mouseReleased( MouseEvent e ) {
            if( draggingItem != null ) {
                // if draggingItem is not null, log dragging file action
                int mouseX = e.getX();
                int mouseY = e.getY();
                
                Point p = SwingUtilities.convertPoint((Component) e.getSource(), e.getPoint(), parent);
                
                if (parent.getBounds().contains(p)){
                	// TODO see if this is right condition for logging. 
                }

                
                draggingItem = findTarget( mouseX, mouseY );
                if( isTargetLabel ) {
                    DefaultMutableTreeNode node = draggingItem.getNode();
                    IconData icon = (IconData) node.getUserObject();
                    CaseNode case_node = (CaseNode) icon.getObject();                    
                }
            }
            draggingItem = null;
        }
    }

    /**
     * ButtonMouseListener class
     */
    class ButtonMouseListener extends MouseAdapter
    {
        @Override
        public void mouseClicked( MouseEvent e ) {
            doArrange();
            UserLogWriter.log2( "Rearrange Button", Location.Favorites, Action.getClicks(e.getClickCount()));
        }

        public void mouseEntered( MouseEvent e ) {
            ((JButton) e.getSource()).setIcon( new ImageIcon( Util.getResourceUrl( "/img/arrangebutton_hovered.png" ) ) );
        }

        public void mouseExited( MouseEvent e ) {
            ((JButton) e.getSource()).setIcon( new ImageIcon( Util.getResourceUrl( "/img/arrangebutton.png" ) ) );
        }

    }

    /**
     * Dropped item entity class
     */
    class Item
    {
        private int x;

        private int y;

        private DefaultMutableTreeNode node;

        private boolean selectedIcon = false;

        private boolean selectedLabel = false;

        private int dragingX = 0;

        private int dragingY = 0;

        public Item( int x, int y, DefaultMutableTreeNode node ) {
            IconData data = (IconData) node.getUserObject();
            this.x = x - 50;
            this.y = y - data.m_icon.getIconHeight() / 2;
            this.node = node;
        }
        
        public String getCaseName() {
        	CaseNode caseNode = (CaseNode) ((IconData) node.getUserObject()).getObject();
        	return caseNode.getName();
        }

        public boolean isIconPosition( int mouseX, int mouseY ) {
            IconData data = (IconData) node.getUserObject();
            Icon icon = (ImageIcon) data.m_icon;
            int width = icon.getIconWidth();
            int height = icon.getIconHeight();
            if( mouseX >= x && mouseX <= x + width && mouseY >= y
                    && mouseY <= y + height ) {
                selectedIcon = true;
                return true;
            } else {
                return false;
            }
        }

        public boolean isLabelPosition( int mouseX, int mouseY ) {
            IconData data = (IconData) node.getUserObject();
            Icon icon = (ImageIcon) data.m_icon;
            int width = icon.getIconWidth();
            int height = icon.getIconHeight();
            CaseNode caseNode = (CaseNode) data.getObject();
            int labelWidth = caseNode.toString().length() * 5;
            // System.out.println("label width = " + labelWidth);
            if( mouseX >= x + width && mouseX <= x + width + labelWidth
                    && mouseY >= y && mouseY <= y + height ) {
                selectedLabel = true;
                dragingX = mouseX - x;
                dragingY = mouseY - y;
                return true;
            } else {
                return false;
            }
        }

        public int getIconWidth() {
            IconData data = (IconData) this.getNode().getUserObject();
            return data.m_icon.getIconWidth();
        }

        public int getIconHeight() {
            IconData data = (IconData) this.getNode().getUserObject();
            return data.m_icon.getIconHeight();
        }

        public int getX() {
            return x;
        }

        public void setX( int mouseX ) {
            x = mouseX;
        }

        public int getY() {
            return y;
        }

        public void setY( int mouseY ) {
            y = mouseY;
        }

        public DefaultMutableTreeNode getNode() {
            return node;
        }

        public boolean isSelectedIcon() {
            return selectedIcon;
        }

        public void setSelectedIcon( boolean flag ) {
            this.selectedIcon = flag;
        }

        public boolean isSelectedLabel() {
            return selectedLabel;
        }

        public void setSelectedLabel( boolean flag ) {
            this.selectedLabel = flag;
        }

    }

}
